#include "Mass_Spring_Animation.h"

/* Qt Includes */
#include <QGridLayout>

/* Sx OpenGL Rendering */
#include <sxOpenGL_DefaultMassSpringMeshRenderer.h>
#include <sxOpenGL_DefaultCameraRenderer.h>
#include <sxOpenGL_DefaultPlotAxesRenderer.h>
#include <sxOpenGL_DefaultBezierCurveRenderer.h>
#include <sxOpenGL_DefaultCuboidRenderer.h>
#include <sxOpenGL_Stored_MeshRenderer.h>
using Sx::Simulation::Objects::Rendering::OpenGL_DefaultMassSpringMeshRenderer; // TODO: This namespace config needs fixed
using Sx::OpenGL::Graphics::Objects::Rendering::OpenGL_DefaultCameraRenderer;
using Sx::OpenGL::Graphics::Objects::Rendering::OpenGL_DefaultPlotAxesRenderer;
using Sx::OpenGL::Graphics::Objects::Rendering::OpenGL_DefaultBezierCurveRenderer;
using Sx::OpenGL::Graphics::Objects::Rendering::OpenGL_DefaultCuboidRenderer;
using Sx::OpenGL::Graphics::Objects::Rendering::OpenGL_Stored_MeshRenderer;

/* Sx Library Collision */
#include <sxCollisionModel.h>
#include <sxDiscreteCollisionModel.h>
#include <sxTriangleSoupCollisionModel.h>
#include <sxMSM_TS_InverseVelocityResponseHandler.h>
#include <sxSelfCCD_MSM_CollisionDetectionHandler.h>
#include <sxMSM_TS_CollisionModel.h>
#include <sxEulerCollisionEventPoolAllocator.h>
using Sx::Simulation::Collision::Models::MSM_TS_CollisionModel;
using Sx::Physics::Collision::Event::EulerCollisionEventPoolAllocator;
using Sx::SelfCCD::Handlers::Detection::SelfCCD_MSM_CollisionDetectionHandler;
using Sx::Simulation::Collision::Handlers::Response::MSM_TS_InverseVelocityResponseHandler;

Mass_Spring_Animation::Mass_Spring_Animation(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags) {
	ui.setupUi(this);
	this->ui.generatePreviewButton->setEnabled(true);
	//--------------------------------------------------------------------------
	// Setup OpenGL Viewports
	//--------------------------------------------------------------------------
	QGridLayout* gridLayout = new QGridLayout();
	gridLayout->setContentsMargins(2, 2, 2, 2);
	this->ui.viewportFrame->setLayout(gridLayout);

	this->openglContext = std::make_shared<OpenGL_Context>();
	this->openglCanvas = std::make_shared<OpenGL_Canvas>(this->openglContext);
	this->openglPickingController = std::make_shared<OpenGL_PickingController>();

	this->viewport_Simulation = new QOpenGL_Viewport(this, this->openglContext);
	this->viewport_Simulation->setRealtime(true);
	gridLayout->addWidget(this->viewport_Simulation);

	this->viewport_CurveEditor = new QOpenGL_Viewport(this, this->viewport_Simulation);
	this->viewport_CurveEditor->setRealtime(true);
	gridLayout->addWidget(this->viewport_CurveEditor);

	//--------------------------------------------------------------------------
	// Setup Collision.
	//--------------------------------------------------------------------------
	MSM_TS_CollisionModel* tscm = new MSM_TS_CollisionModel();
	EulerCollisionEventPoolAllocator<MassSpringMesh>* eulerAllocator = new EulerCollisionEventPoolAllocator<MassSpringMesh>();

	SelfCCD_MSM_CollisionDetectionHandler* ccd_msm_cdh = new SelfCCD_MSM_CollisionDetectionHandler();
	MSM_TS_InverseVelocityResponseHandler* msm_ts_ivrh = new MSM_TS_InverseVelocityResponseHandler();

	tscm->setCollisionDetectionHanlder(ccd_msm_cdh);
	tscm->setCollisionResponseHandler(msm_ts_ivrh);
	tscm->setAllocator(eulerAllocator);
	
	//--------------------------------------------------------------------------
	// Create a new mass-spring simulation.
	//--------------------------------------------------------------------------
	this->massSpringSimulation = std::make_shared<MassSpringSimulation>(tscm);
	this->massSpringSimulation->setTimeStepType(Sx::Simulation::FIXED);
	this->massSpringSimulation->setTimeStep(1.0 / 1000.0f);
	this->massSpringSimulation->pause();

	this->objStorage = std::make_shared<OBJ_VegaMassSpringMeshStorage>();

	this->object1 = MassSpringMesh::Load(this->objStorage.get(), std::make_shared<OpenGL_DefaultMassSpringMeshRenderer>(openglContext, 1.0f, 0.2f, 0.1f), "t1.obj");
	this->object1->getObject()->setColor(0.3f, 0.5f, 0.9f);
	this->object1->addPosition(0.0f, 2.0f, 0.0f);
	this->object2 = MassSpringMesh::Load(this->objStorage.get(), std::make_shared<OpenGL_DefaultMassSpringMeshRenderer>(openglContext, 0.5f, 1.0f, 0.3f), "t2.obj");
	this->object2->getObject()->setColor(0.0f, 1.0f, 0.2f);
	//this->object3 = MassSpringMesh::Load(this->objStorage.get(), std::make_shared<OpenGL_DefaultMassSpringMeshRenderer>(openglContext, 0.0f, 0.5f, 1.0f), "t3.obj");
	//this->object3->getObject()->setColor(1.0f, 0.2f, 0.5f);
	this->ground = MassSpringMesh::Load(this->objStorage.get(), std::make_shared<OpenGL_DefaultMassSpringMeshRenderer>(openglContext, 0.2f, 0.2f, 0.4f), "ground.obj");
	this->ground->getObject()->setColor(0.25f, 0.25f, 0.25f);

	this->object1->setSubsteps(8);
	this->object2->setSubsteps(8);
	//this->object3->setSubsteps(8);
	this->ground->setStatic(true);

	this->massSpringSimulation->add(this->ground);
	this->massSpringSimulation->add(this->object1);
	this->massSpringSimulation->add(this->object2);
	//this->massSpringSimulation->add(this->object3);
	
	// RECORDING is first
	this->testRecording = std::make_shared<MassSpringSimulationRecording>();

	//--------------------------------------------------------------------------
	// Timeline-Based Curve Editor
	//--------------------------------------------------------------------------
	this->curveEditor = std::make_shared<FixedTimelineCurveController>(
		this->viewport_CurveEditor, 
		this->openglCanvas, 
		this->openglPickingController, 
		std::make_shared<OpenGL_DefaultCameraRenderer>(this->openglContext), 
		std::make_shared<OpenGL_DefaultPlotAxesRenderer>(this->openglContext), 
		std::make_shared<OpenGL_DefaultCuboidRenderer>(this->openglContext),
		std::make_shared<OpenGL_DefaultBezierCurveRenderer>(this->openglContext)
	);

	this->curveEditor->initialize();
	this->curveEditor->setDivisions(60);
	this->curveEditor->setFrameCount(600);
	this->curveEditor->setCurrentFrame(0);

	//--------------------------------------------------------------------------
	// Recorder
	//--------------------------------------------------------------------------
	this->mssRecorder = std::make_shared<MassSpringSimulationRecorder>(this->massSpringSimulation, this->testRecording);
	this->mssRecorder->addTimeline(this->curveEditor);



	//--------------------------------------------------------------------------
	// MSS Edit Controller
	//--------------------------------------------------------------------------
	this->mssEditController = std::make_shared<MSS_ReplayEditController>(
		this->viewport_Simulation, 
		this->openglCanvas, 
		this->openglPickingController, 
		std::make_shared<OpenGL_DefaultCameraRenderer>(this->openglContext), 
		this->massSpringSimulation, 
		this->testRecording,
		this->mssRecorder,
		std::make_shared<OpenGL_Stored_MeshRenderer>(this->openglContext));

	this->mssEditController->initialize();
	this->mssEditController->addTimeline(this->curveEditor);
	this->curveEditor->setReplayable(this->mssEditController);

	//--------------------------------------------------------------------------
	// Last Connections
	//--------------------------------------------------------------------------
	this->viewport_Simulation->setController(this->mssEditController);
	this->viewport_CurveEditor->setController(this->curveEditor);

	this->viewport_Simulation->setClearColor(0.2f, 0.2f, 0.2f);
	this->viewport_CurveEditor->setClearColor(0.2f, 0.2f, 0.2f);
}

Mass_Spring_Animation::~Mass_Spring_Animation() {}

void Mass_Spring_Animation::onStartRecording() {
	this->mssRecorder->setRecording(true);
	this->curveEditor->setInputEnabled(false);
	this->massSpringSimulation->resume();

	// UI
	this->ui.startRecordingButton->setEnabled(false);
	this->ui.stopRecordingButton->setEnabled(true);
}

void Mass_Spring_Animation::onStopRecording() {
	this->mssRecorder->setRecording(false);
	this->mssEditController->pause();
	this->curveEditor->setInputEnabled(true);

	// UI
	this->ui.startRecordingButton->setEnabled(false);
	this->ui.stopRecordingButton->setEnabled(true);
	this->ui.playButton->setEnabled(true);
	this->ui.pauseButton->setEnabled(true);
	this->ui.resetButton->setEnabled(true);
	this->ui.incrementButton->setEnabled(true);
	this->ui.decrementButton->setEnabled(true);
	this->ui.applyForceCurveButton->setEnabled(true);
	this->ui.generatePreviewButton->setEnabled(true);

	unsigned int totalFrames = this->testRecording->getFrameCount();
	std::cout << "[RECORDING] Recorded: " << totalFrames << " frames" << std::endl;
	this->curveEditor->setDivisions(60);
	this->curveEditor->setFrameCount(totalFrames);
}

void Mass_Spring_Animation::onPlay() {
	this->mssEditController->setFrameIndex(this->curveEditor->getCurrentTime());
	this->mssEditController->resume();
}

void Mass_Spring_Animation::onPause() {
	this->mssEditController->pause();
}

void Mass_Spring_Animation::onResetTime() {
	this->mssEditController->setFrameIndex(0);
	this->mssEditController->pause();
}

void Mass_Spring_Animation::onTimeIncrement() {
	this->mssEditController->pause();
	this->mssEditController->incrementFrameIndex();
}

void Mass_Spring_Animation::onTimeDecrement() {
	this->mssEditController->pause();
	this->mssEditController->decrementFrameIndex();
}

void Mass_Spring_Animation::onApplyForceCurve() {
	this->massSpringSimulation->zeroAllExternalForces();
	this->mssEditController->applyExternalForce(this->curveEditor);
}

void Mass_Spring_Animation::onGeneratePreview() {
	// generate 120 frames @ 60fps recording every 30th frame.
	this->mssEditController->generateSimulationPreview(60 * 10, 60);
}

void Mass_Spring_Animation::onClearPreview() {
	this->mssEditController->clearSimluationPreview();
}
